using System;
using System.Collections.Generic;
using System.Linq;
#if !SILVERLIGHT
using System.Runtime.Serialization;
#endif
using System.Reflection;
using System.Text;
using Gko.Utils.Interfaces;

namespace Gko.Utils
{
#if !SILVERLIGHT
	[Serializable]
#endif

	public class WeakAction<T>
#if SILVERLIGHT
		:		
#else
		: WeakReference ,
#endif
		IWeakAction
	{
#if SILVERLIGHT
		private readonly WeakReference reference;
#endif
		private readonly MethodInfo method;

		public WeakAction(Action<T> action)
#if !SILVERLIGHT
			: base(action.Target)
		{
#else
		{
			if ((action.Method.Attributes & MethodAttributes.Public) != MethodAttributes.Public) {
				throw new ArgumentException("Method must be public");
			}
			this.reference = new WeakReference(action.Target);
#endif
			this.method = action.Method;
		}

		#region IWeakAction Members
#if SILVERLIGHT

		public bool IsAlive
		{
			get
			{
				return this.reference.IsAlive;
			}
		}

		public object Target
		{
			get
			{
				return this.reference.Target;
			}
		}

#endif
		public bool IsStatic
		{
			get
			{
				return this.method.IsStatic;
			}
		}

		Delegate IWeakAction.CreateAction()
		{
			return this.CreateAction();
		}

		bool IWeakAction.TryCreateAction(out Delegate action)
		{
			Action<T> a;
			var res = this.TryCreateAction(out a);
			action = a;
			return res;
		}

		#endregion

#if !SILVERLIGHT
		protected WeakAction(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
			this.method = (MethodInfo)info.GetValue("method", typeof(MethodInfo));
		}

		public override void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			base.GetObjectData(info, context);
			info.AddValue("method", this.method);			
		}
#endif

		public Action<T> CreateAction()
		{
			return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), this.Target, this.method.Name);
		}

		public bool TryCreateAction(out Action<T> action)
		{
			action = default(Action<T>);
			if (this.IsStatic) {
				try {
					action = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), this.method.DeclaringType, this.method.Name);
					return true;
				} catch (Exception) {
				}
			}
			if (this.IsAlive) {
				try {
					action = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), this.Target, this.method.Name);
					return true;
				} catch (Exception) {
				}
			}
			return false;
		}
	}
}
